using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Dts.Runtime;

namespace SsisUtilities.PacMan.Components
{
    public class PackageCollectionUtil : List <PackageUtil>
    {
        /// <summary>
        /// Defines scopes for what packages may be affected by a given operation
        /// </summary>
        public enum AffectedScope
        {
            /// <summary>
            /// No packages in the collection will be affected
            /// </summary>
            None,
            /// <summary>
            /// Some, but not all, packages in the collection will be affected
            /// </summary>
            Some,
            /// <summary>
            /// All packages in the collection will be affected
            /// </summary>
            All
        }

        #region Public Properties

        private string rootCollectionPath;

        public string RootCollectionPath
        {
            get { return rootCollectionPath; }
            set { rootCollectionPath = value; }
        }

        #endregion
        
        #region Constructors

        public PackageCollectionUtil(FileInfo projectFile)
        {
            List<PackageUtil> packages;

            if (projectFile.Extension.ToLower() == ".dtproj")
            {
                packages = DtprojHelper.GetPackageUtils(projectFile);
            }
            else if (projectFile.Extension.ToLower() == ".sln")
            {
                packages = SlnHelper.GetPackageUtils(projectFile);
            }
            else
            {
                throw new ApplicationException(string.Format(
                    "An unexpected file type '{0}' was supplied.", 
                    projectFile.Extension));
            }

            rootCollectionPath = projectFile.DirectoryName;
            BuildCollection(packages);
        }

        public PackageCollectionUtil(DirectoryInfo rootFolder)
        {
            List<FileInfo> packageList = FolderHelper.GetPackages(rootFolder);

            rootCollectionPath = rootFolder.FullName;
            BuildCollection(packageList);
        }

        public PackageCollectionUtil(PackageUtil package)
        {
            this.Add(package);
        }


        #endregion

        #region Private Worker Routines

        private void BuildCollection(List<FileInfo> packageFiles)
        {
            foreach (FileInfo packageFile in packageFiles)
            {
                this.Add(new PackageUtil(packageFile));
            }
        }

        private void BuildCollection(List<FileInfo> packageFiles,
            string projectName)
        {
            foreach (FileInfo packageFile in packageFiles)
            {
                this.Add(new PackageUtil(packageFile, projectName));
            }
        }

        private void BuildCollection(List<PackageUtil> packages)
        {
            foreach (PackageUtil package in packages)
            {
                this.Add(package);
            }
        }

        #endregion

        #region SetVersion Overloads

        public void SetVersion(int versionMajor, int versionMinor,
            string versionComments, bool resetVersionBuild)
        {
            foreach (PackageUtil package in this)
            {
                package.SetVersion(versionMajor, versionMinor, 
                    versionComments, resetVersionBuild);
            }
        }

        public void SetVersion(int versionMajor, int versionMinor,
            string versionComments)
        {
            foreach (PackageUtil package in this)
            {
                package.SetVersion(versionMajor, versionMinor, 
                    versionComments, false);
            }
        }

        public void SetVersion(int versionMajor, int versionMinor)
        {
            foreach (PackageUtil package in this)
            {
                package.SetVersion(versionMajor, versionMinor, 
                    string.Empty, false);
            }
        }

        #endregion

        #region CreateVariable Overloads

        public void CreateVariable(string variableName, TypeCode dataType,
            object value)
        {
            foreach (PackageUtil package in this)
            {
                package.CreateVariable(variableName, dataType, false,
                    "User", value, false, string.Empty, string.Empty);
            }
        }

        public void CreateVariable(string variableName, TypeCode dataType,
            bool readOnly, string variableNamespace, object value)
        {
            foreach (PackageUtil package in this)
            {
                package.CreateVariable(variableName, dataType, readOnly,
                    variableNamespace, value, false, 
                    string.Empty, string.Empty);
            }
        }

        public void CreateVariable(string variableName, TypeCode dataType,
            bool readOnly, string variableNamespace, object value,
            bool evaluateAsExpression, string expression, 
            string description)
        {
            foreach (PackageUtil package in this)
            {
                package.CreateVariable(variableName, dataType, readOnly,
                    variableNamespace, value, evaluateAsExpression,
                    expression, description);
            }        
        }

        #endregion

        #region VariableExists Overloads

        public AffectedScope VariableExists(string variableName, string variableNamespace)
        {
            bool areSomeAffected = false;
            bool areSomeNotAffected = false;

            foreach (PackageUtil package in this)
            {
                if (package.VariableExists(variableName, variableNamespace))
                {
                    areSomeAffected = true;
                }
                else
                {
                    areSomeNotAffected = true;
                }
            }

            if (areSomeAffected && areSomeNotAffected)
            {
                return AffectedScope.Some;
            }
            else if (areSomeAffected)
            {
                return AffectedScope.All;
            }
            else
            {
                return AffectedScope.None;
            }
        }

        public AffectedScope VariableExists(string variableName)
        {
            bool areSomeAffected = false;
            bool areSomeNotAffected = false;

            foreach (PackageUtil package in this)
            {
                if (package.VariableExists(variableName))
                {
                    areSomeAffected = true;
                }
                else
                {
                    areSomeNotAffected = true;
                }
            }

            if (areSomeAffected && areSomeNotAffected)
            {
                return AffectedScope.Some;
            }
            else if (areSomeAffected)
            {
                return AffectedScope.All;
            }
            else
            {
                return AffectedScope.None;
            }
        }
        
        public AffectedScope VariableExists(Variable variable)
        {
            return VariableExists(variable.Name, variable.Namespace);
        }

        #endregion

        #region DeleteVariable Overloads

        public void DeleteVariable(Variable variable)
        {
            foreach (PackageUtil package in this)
            {
                package.DeleteVariable(variable);
            }
        }

        public void DeleteVariable(string variableName)
        {
            foreach (PackageUtil package in this)
            {
                package.DeleteVariable(variableName);
            }
        }

        public void DeleteVariable(string variableName,
            string variableNamespace)
        {
            foreach (PackageUtil package in this)
            {
                package.DeleteVariable(variableName, variableNamespace);
            }
        }

        #endregion


        public void CopyScriptTaskPrototype(PackageUtil sourcePackage)
        {
            foreach (PackageUtil package in this)
            {
                package.CopyScriptTaskPrototype(sourcePackage);
            }
        }


        public List<string> GetDiscreteExecutableTypes()
        {
            List<string> executables = new List<string>();

            foreach (PackageUtil package in this)
            {
                foreach (string exeTypeName in package.GetDiscreteExecutableTypes())
                {
                    if (!(string.IsNullOrEmpty(exeTypeName)) && !(executables.Contains(exeTypeName)))
                    {
                        executables.Add(exeTypeName);
                    }
                }
            }

            return executables;
        }

        public List<DTSExecResult> Validate()
        {
            List<DTSExecResult> validationResults = new List<DTSExecResult>();

            foreach (PackageUtil package in this)
            {
                validationResults.Add(package.Validate());
            }
            return validationResults;
        }

        public List<DTSExecResult> CustomValidate()
        {
            List<DTSExecResult> validationResults = new List<DTSExecResult>();

            foreach (PackageUtil package in this)
            {
                validationResults.Add(package.CustomValidate());
            }
            return validationResults;
        }

        public List<string> GetValidateScript()
        {
            List<string> validationStripts = new List<string>();

            foreach (PackageUtil package in this)
            {
                validationStripts.Add(package.GetValidateScript());
            }
            return validationStripts;
        }

        public SortedDictionary<string, Variable> GetVariables()
        {
            SortedDictionary<string, Variable> variables = new SortedDictionary<string, Variable>();

            foreach (PackageUtil package in this)
            {
                foreach (Variable newVar in package.GetVariables())
                {
                    if (!variables.ContainsKey(newVar.Name))
                    {
                        variables.Add(newVar.Name, newVar);
                    }
                }
            }  

            return variables;
        }

        public List<Configuration> GetConfigurations()
        {
            List<Configuration> configurations = new List<Configuration>();

            foreach (PackageUtil package in this)
            {
                foreach (Configuration newConf in package.GetConfigurations())
                {
                    // We can't use variables.Contains here because it's 
                    // checking reference equality, not by name

                    bool alreadyExists = false;

                    foreach (Configuration existingConf in configurations)
                    {
                        if (newConf.Name == existingConf.Name)
                        {
                            alreadyExists = true;
                            break;
                        }
                    }

                    if (!alreadyExists)
                    {
                        configurations.Add(newConf);
                    }
                }
            }
            return configurations;
        }

        public List<ConnectionManager> GetConnectionManagers()
        {
            return GetConnectionManagers(false);
        }

        public List<ConnectionManager> GetConnectionManagers(bool oleDbOnly)
        {
            List<ConnectionManager> connectionManagers =
                new List<ConnectionManager>();

            foreach (PackageUtil package in this)
            {
                foreach (ConnectionManager newConn in package.GetConnectionManagers(oleDbOnly))
                {
                    // We can't use .Contains here because it's 
                    // checking reference equality, not by name

                    bool alreadyExists = false;

                    foreach (ConnectionManager existingConn in connectionManagers)
                    {
                        if (newConn.Name == existingConn.Name)
                        {
                            alreadyExists = true;
                            break;
                        }
                    }

                    if (!alreadyExists)
                    {
                        foreach (DtsProperty foo in newConn.Properties)
                        {
                            System.Diagnostics.Debug.WriteLine(foo.Name);
                        }
                        connectionManagers.Add(newConn);
                    }
                }
            }
            return connectionManagers;
        }

        public void DeleteConfiguration(string configurationName)
        {
            foreach (PackageUtil package in this)
            {
                package.DeleteConfiguration(configurationName);
            }
        }

        public void UpdateConnectionManagerExpression(
            string connectionManagerName, string propertyName,
            string expression)
        {
            foreach (PackageUtil package in this)
            {
                package.UpdateConnectionManagerExpression(connectionManagerName,
                    propertyName, expression);
            }
        }

        /// <summary>
        /// Apply the selected configuration to each of the supplied packages
        /// </summary>
        /// <param name="ssisPackage"></param>
        /// <param name="configurationName"></param>
        /// <remarks>
        /// The default configuration will also always be applied
        /// </remarks>
        public void ApplyConfigurationSubset(string xmlConfigFileName, string configurationSubsetName)
        {
            foreach (PackageUtil package in this)
            {
                package.ApplyConfigurationSubset(xmlConfigFileName, configurationSubsetName);
            }
        }

        public Dictionary<string, string> GetConfiguredVariableValues(
            string configurationSubsetName, string xmlConfigFileName)
        {
            Dictionary<string, string> variablesToConfigure = new Dictionary<string, string>();

            foreach (PackageUtil package in this)
            {
                Dictionary<string, string> newVars =
                    package.GetConfiguredVariableValues(configurationSubsetName,
                    xmlConfigFileName);

                foreach (KeyValuePair<string, string> varPair in newVars)
                {
                    if (variablesToConfigure.ContainsKey(varPair.Key))
                    {
                        //  The variable is already in the collection - update its value
                        variablesToConfigure[varPair.Key] = varPair.Value;
                    }
                    else
                    {
                        //  The variable is not yet in the collection - add it
                        variablesToConfigure.Add(varPair.Key, varPair.Value);
                    }
                }
                
            }
            return variablesToConfigure;
        }


        public SortedDictionary<string, Variable> GetVariableValues(string variableName)
        {
            SortedDictionary<string, Variable> variables = new SortedDictionary<string, Variable>();

            foreach (PackageUtil package in this)
            {
                if (package.VariableExists(variableName))
                {
                    try
                    {
                        variables.Add(package.PackageFilePath, package.SsisPackage.Variables[variableName]);
                    }
                    catch (Exception ex)
                    {
                        const string ERROR_TEMPLATE = "Exception {0} occured in package with name {1} defined in file {2} when listing variable named {3}";
                        System.Diagnostics.Debug.WriteLine(string.Format(ERROR_TEMPLATE,
                            ex.Message, package.SsisPackage.Name, package.PackageFileName, variableName));
                    }
                }
            }

            return variables;
        }

        public SortedDictionary<string, List<string>> GetPackageIDs()
        {
            SortedDictionary<string, List<string>> ids = new SortedDictionary<string, List<string>>();

            foreach (PackageUtil package in this)
            {
                string id = package.SsisPackage.ID;
                if (!ids.ContainsKey(id))
                {
                    // This is a new key - add a new dictionary 
                    ids.Add(id, new List<string>());
                }
                // either way, add the package path
                ids[id].Add(package.PackageFilePath);

            }


            return ids;
        }


        public void Save()
        {
            foreach (PackageUtil package in this)
            {
                package.Save();
            }        
        }

    }
}
